package information;



import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import util.Coordinaat;
import util.WindRichting;

public class Kaart {

	/**
	 * Maak nieuwe kaart.
	 */
	public Kaart() {
		
		kaart = new HashMap<Coordinaat, Vakje>();
		Coordinaat beginCoordinaat = new Coordinaat(0,0);
		maakNieuwVakjeEnVoegToe(beginCoordinaat);
	}
	
	/**
	 * Geeft vakje op cartesische coordinaat.
	 * Als het vakje nog niet bestaat wordt die aangemaakt.
	 * 
	 * @return	return != null
	 */
	public Vakje getVakjeOp(Coordinaat coordinaat) {
		Vakje vakje = getRauwVakjeOp(coordinaat);
		
		if(vakje == null)
			return maakNieuwVakjeEnVoegToe(coordinaat);
		
		return vakje;
	}
	
	/**
	 * Geeft het vakje op cartesische coordinaat.
	 * Als het vakje nog niet bestaat wordt null terug gegeven.
	 * 
	 * @return	return == null || return != null
	 */
	public Vakje getRauwVakjeOp(Coordinaat coordinaat)
	{
		return kaart.get(coordinaat);
	}
	
	/**
	 * Maakt een nieuw vakje aan op het gegeven coordinaat.
	 * Het vakje wordt ook toegevoegd aan de kaart.
	 * 
	 * @return	return != null
	 */
	private Vakje maakNieuwVakjeEnVoegToe(Coordinaat coordinaat)
	{
		Vakje vakje = maakNieuwVakje(coordinaat);
		
		vakjeToevoegen(vakje);
		
		return vakje;
	}
	
	/**
	 * Maakt een nieuw vakje aan op het gegeven coordinaat.
	 * 
	 * @return	return != null
	 */
	protected Vakje maakNieuwVakje(Coordinaat coordinaat)
	{
		return new Vakje(coordinaat, this);
	}
	
	/**
	 * Voegt een vakje toe aan de kaart.
	 */
	private void vakjeToevoegen(Vakje vakje)
	{
		kaart.put(vakje.getCoordinaat(), vakje);
		updateMinMax(vakje.getCoordinaat());
	}
	
	/**
	 * Update de minimum en maximum X en Y waarden. Wanneer
	 * de gegeven coordinaat buiten het vorig bereik valt.
	 */
	private void updateMinMax(Coordinaat coordinaat)
	{
		if(coordinaat.getX() < minX)
			setMinimaalXCoordinaat(coordinaat.getX());
		
		if(coordinaat.getX() > maxX)
			setMaximaalXCoordinaat(coordinaat.getX());
		
		if(coordinaat.getY() < minY)
			setMinimaalYCoordinaat(coordinaat.getY());
		
		if(coordinaat.getY() > maxY)
			setMaximaalYCoordinaat(coordinaat.getY());
	}
	
	private int minX = 0;
	private int minY = 0;
	private int maxX = 0;
	private int maxY = 0;
	
	
	private Map<Coordinaat,Vakje> kaart;

	/**
	 * Geeft de Y-coordinaat terug van het vakje dat het meest
	 * vanboven ligt.
	 */
	public int getMaximaleYCoordinaat() {
		return maxY;
	}

	/**
	 * Geeft de Y-coordinaat terug van het vakje dat het meest
	 * naar rechts ligt.
	 */
	public int getMaximaleXCoordinaat() {
		return maxX;
	}

	/**
	 * Geeft de Y-coordinaat terug van het vakje dat het meest
	 * vanonder ligt.
	 */
	public int getMinimaalYCoordinaat() {
		return minY;
	}

	/**
	 * Geeft de Y-coordinaat terug van het vakje dat het meest
	 * naar links ligt.
	 */
	public int getMinimaalXCoordinaat() {
		return minX;
	}
	
	public Coordinaat getBovensteVerkend(){
		for(int y = getMaximaleYCoordinaat(); y >= getMinimaalYCoordinaat(); y++)
			for(int x = getMinimaalXCoordinaat(); x <= getMaximaleXCoordinaat(); x++)
			{
				Vakje maxVakje = getRauwVakjeOp(new Coordinaat(x,y));
				if(maxVakje != null && maxVakje.isVolledigGekend())
					return maxVakje.getCoordinaat();
			}
		throw new IllegalStateException("Maximale Y-coordinaat is niet consistent");
	}

	/**
	 * Stelt de nieuwe minimale X-coordinaat in en maakt
	 * ook tussenliggende vakjes aan.
	 */
	private void setMinimaalXCoordinaat(int minX)
	{
		int oud = getMinimaalXCoordinaat();
		this.minX = minX;
		
		maakKolom(minX, oud);
	}
	
	/**
	 * Stelt de nieuwe maximale X-coordinaat in en maakt
	 * ook tussenliggende vakjes aan.
	 */
	private void setMaximaalXCoordinaat(int maxX)
	{
		int oud = getMaximaleXCoordinaat();
		this.maxX = maxX;
		
		maakKolom(oud, maxX);
	}
	
	/**
	 * Stelt de nieuwe minimale Y-coordinaat in en maakt
	 * ook tussenliggende vakjes aan.
	 */
	private void setMinimaalYCoordinaat(int minY)
	{
		int oud = getMinimaalYCoordinaat();
		this.minY = minY;
		
		maakRij(minY, oud);
	}
	
	/**
	 * Stelt de nieuwe maximale Y-coordinaat in en maakt
	 * ook tussenliggende vakjes aan.
	 */
	private void setMaximaalYCoordinaat(int maxY)
	{
		int oud = getMaximaleYCoordinaat();
		this.maxY = maxY;
		
		maakRij(oud, maxY);
	}
	
	/**
	 * Maakt eventueel nieuwe kolommen aan tussen xMin
	 * en xMax.
	 */
	private void maakKolom(int xMin, int xMax)
	{
		for(int x = xMin; x <= xMax; x++)
			for(int y = getMinimaalYCoordinaat(); y <= getMaximaleYCoordinaat(); y++)
			{
				getVakjeOp(new Coordinaat(x, y));
			}
	}
	
	/**
	 * Maakt eventueel nieuwe rijen aan tussen xMin
	 * en xMax.
	 */
	private void maakRij(int yMin, int yMax)
	{
		for(int y = yMin; y <= yMax; y++)
			for(int x = getMinimaalXCoordinaat(); x <= getMaximaleXCoordinaat(); x++)
			{
				getVakjeOp(new Coordinaat(x, y));
			}
	}
	
	/**
	 * Geeft alle vakjes terug binnen het bereik van X en Y
	 * minimaal en maximaal.
	 */
	public List<Vakje> getAlleGekendeVakjes() {
		
		List<Vakje> lijst = new ArrayList<Vakje>();
		
		for(int x = getMinimaalXCoordinaat(); x <= getMaximaleXCoordinaat(); x++)
		{
			for(int y = getMinimaalYCoordinaat(); y <= getMaximaleYCoordinaat(); y++)
			{
				Vakje rauwVakje = getRauwVakjeOp(new Coordinaat(x, y));
				
				if(rauwVakje != null)
					lijst.add(rauwVakje);
			}
		}
			
		return lijst;
	}
	
	private Coordinaat laatsteLocatiePacMan = null;

	/**
	 * Geeft de laatste positie van pacman terug.
	 */
	public Coordinaat getLaatsteLocatiePacMan() {
		return this.laatsteLocatiePacMan;
	}
	
	/**
	 * Stelt de laatste positie van pacman in.
	 */
	public void setLaatsteLocatiePacMan(Coordinaat laatsteLocatiePacMan) {
		this.laatsteLocatiePacMan = laatsteLocatiePacMan;
	}

	
	@Override
	public String toString()
	{
		StringBuilder bouwer = new StringBuilder();
		
		
		
		for(int y = getMaximaleYCoordinaat(); y >= getMinimaalYCoordinaat(); y--)
		{
			
			StringBuilder boven = new StringBuilder();
			StringBuilder midden = new StringBuilder();
			
			for(int x = getMinimaalXCoordinaat(); x <= getMaximaleXCoordinaat(); x++)
			{
				Vakje vakje = getVakjeOp(new Coordinaat(x, y));
				boven.append('+');
				
				VakGrens muurBoven = vakje.getVakGrens(WindRichting.NOORD);
				if(!muurBoven.isGekend())
					boven.append("???");
				else
				{
					if(muurBoven.isMuur())
					{
						boven.append("---");
					}
					else
					{
						boven.append("   ");
					}
				}
				
				VakGrens muurLinks = vakje.getVakGrens(WindRichting.WEST);
				if(!muurLinks.isGekend())
					midden.append("?");
				else
				{
					if(muurLinks.isMuur())
					{
						midden.append("|");
					}
					else
					{
						midden.append(" ");
					}
				}
				
				midden.append(' ');
				
				if(x == 0 && y == 0)
					midden.append("X");
				else
					midden.append(' ');
				
				midden.append(' ');
			}
			
			VakGrens muurRechts = getVakjeOp(new Coordinaat(getMaximaleXCoordinaat(), y)).getVakGrens(WindRichting.OOST);
			
			boven.append('+');
			
			if(!muurRechts.isGekend())
				midden.append("?");
			else
			{
				if(muurRechts.isMuur())
				{
					midden.append("|");
				}
				else
				{
					midden.append(" ");
				}
			}
			
			bouwer.append(boven);
			bouwer.append("\n");
			bouwer.append(midden);
			bouwer.append("\n");
		}
		
		//Onderste stuk
		
		for(int x = getMinimaalXCoordinaat(); x <= getMaximaleXCoordinaat(); x++)
		{
			bouwer.append('+');
			VakGrens muur = getVakjeOp(new Coordinaat(x, 0)).getVakGrens(WindRichting.ZUID);
			
			if(!muur.isGekend())
				bouwer.append("???");
			else
			{
				if(muur.isMuur())
				{
					bouwer.append("---");
				}
				else
				{
					bouwer.append("   ");
				}
			}
		}
		
		bouwer.append('+');
		
		return bouwer.toString();
	}
}